Solid 2.0 support across the toolchain, 0.2.0 - #5
Merged
Conversation
added 7 commits
August 14, 2026 13:13
The message was "must declare its public Layout exports", which reads as a missing required field. It sends you to write the list, when the question worth asking is whether you wanted source mode at all: convention-based discovery needs no such list, and the documentation says so in as many words. It also said nothing about the cost of keeping one. Nothing compares the list to the barrel, so a rename that misses it leaves the manifest naming components that no longer exist while rejecting the ones that do, with no diagnostic anywhere. That is worth knowing before you choose the mode, not after. The message now names the trade and points at both ways out.
Recorded on the branch rather than in a private note, because the number that matters contradicts the assumption: this was scoped as "three packages need Solid 2 support" and the whole dependence is 32 sites in one 437-line file, plus two lines of Rust. The rsbuild plugin has none at all. The one genuinely hard piece is the four-bucket splitProps that routes every prop through the runtime; omit() returns only a remainder, so the bucket boundaries have to be re-derived rather than falling out of the call.
The runtime used four things Solid 2.0 changed, and three of them can be
told apart at runtime from the module object itself: `splitProps` became
`omit` and lost its subset half, `Context.Provider` became the context,
and `useContext` now throws where it used to return `undefined`. Those
are handled in place - detected once at load, or spelled so both majors
read it the same way, which is why `createContext<UIConfig>({})` now
carries a default it never needed before.
The fourth cannot. 1.9 serves `Dynamic` and `createComponent` from
`solid-js/web`; 2.0 moved them to `@solidjs/web` and dropped the old
subpath, so no single import statement resolves under both. That one
module is now `renderer.ts`, and the build emits the tree twice with its
2.0 twin swapped in. `dist/component.js` and `dist/solid-2/component.js`
are identical files; the renderer is the whole difference.
The prop routing is the part that changed shape rather than name. One
`splitProps` call returned all four buckets and made them disjoint by
construction. `omit` returns only the remainder, so the three routed
buckets are picked by name and the bucket each declared key belongs to is
decided once per component instead of falling out of the call.
145 tests pass under 1.9, unchanged. The 2.0 output is emitted and its
declarations bind to `@solidjs/web`, but `skipLibCheck` means it has not
been checked against an installed solid-js 2 yet.
The runtime ships twice because Solid 2.0 moved `Dynamic` and `createComponent` out of `solid-js/web` and dropped that subpath. A generated component has to import its boundary from the matching entry, and the compiler is what writes that import, so it needs to be told which major the build targets: `solid: 2` emits `solid-layouts/solid-2/application-boundary` in place of `solid-layouts/application-boundary`. Told rather than sniffed. The compiler sees one source file, and which Solid an application runs on is not written in it. Default is 1 and the emission is byte-identical to before, which the test asserts directly rather than by inspection: a host that has never heard of this option keeps getting what it always got. The builtins list gains 2.0's renames - Suspense to Loading, SuspenseList to Reveal, ErrorBoundary to Errored, plus Repeat - and the source check admits `@solidjs/web`. Neither is gated behind the option, because the check tests a name against a module and no 1.9 build can import `Loading` from a Solid module that does not export one. 60 Rust tests and 21 JS tests pass. Verified through the rebuilt binding, not only through the Rust: all three of unset, 1 and 2 emit what they should.
`pluginSolid2LayoutsLibrary` and `pluginSolid2LayoutsApplication`, beside
the existing pair rather than in place of it. They thread `solid: 2`
through to the compiler, so the library emits its boundary import against
`solid-layouts/solid-2/application-boundary` and the application aliases
that specifier to the runtime's `./solid-2` entry instead of its root.
Separate names rather than an option on the old ones, because the choice
is not independent of the rest of the build: it has to agree with
`pluginSolid2()` and with the installed `solid-js`. A name that must match
its neighbour in the plugin list is easier to get right, and to read back
later, than a flag that must:
pluginSolid2LayoutsLibrary(),
pluginBabel({ include: /\.(?:jsx|tsx)$/ }),
pluginSolid2(),
Asking a runtime that predates this for `./solid-2` now says so by name
rather than quietly falling back to `main` and serving the wrong major.
Both modes verified against the real compiler, not only the plumbing:
bundle mode through a new test in library.test.js, and source mode - the
one @pathscale/ui uses - by generating a fixture both ways and reading the
header. Unset and 2 differ in exactly that one specifier and nowhere else.
Minor rather than major, and not a prerelease: 1.9 is still the default on every entry point, `pluginSolidLayoutsLibrary` and its application twin are untouched, and the compiler emits byte-identical output when `solid` is unset. Nothing that installs 0.1.x breaks on 0.2.0. All three move together because the feature spans all three: the runtime gained a second build, the compiler gained the option that picks it, and the plugin gained the pair of names that sets the option. `index.js` regenerated - napi bakes the package version into the binding loader's mismatch check, so a bump that skips it publishes a loader that rejects its own binary.
pathscale
force-pushed
the
next/solid-2
branch
from
August 15, 2026 00:02
e4b8c10 to
137adab
Compare
pathscale
changed the base branch from
master
to
fix/name-the-source-mode-manifest
August 15, 2026 00:02
Owner
Author
|
Stacked on #4. Review that one first; this PR's diff is against its branch, not master. |
pathscale
changed the base branch from
fix/name-the-source-mode-manifest
to
master
August 15, 2026 00:43
Owner
Author
|
Folded #4 into this PR rather than stacking, so this is the single PR: 8 commits, the source-mode message fix plus Solid 2.0 support across all three packages, targeting master. CI runs again now that the base is master. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Solid 2.0 support to all three packages without changing what a Solid 1.9 build does. 1.9 stays the default on every entry point,
pluginSolidLayoutsLibraryand its application twin are untouched, and the compiler emits byte-identical output whensolidis unset.The shape
Of the four things Solid 2.0 changed that this runtime used, three are detectable at runtime from the module object and needed no fork:
splitPropsbecameomit"omit" in solidContext.Providerbecame the contextcontext.Provider ?? contextuseContextthrows instead of returningundefined{}, which both majors read the same wayDynamicandcreateComponentmoved moduleThe fourth cannot be, because a module specifier resolves before any code runs. 1.9 serves them from
solid-js/web; 2.0 moved them to@solidjs/weband dropped thesolid-js/websubpath entirely. That is nowsrc/renderer.ts, twenty lines, with a twin.scripts/build.mjsemits the tree twice with the twin swapped in, anddist/component.jsanddist/solid-2/component.jsare identical files.Selection is an explicit specifier rather than a bundler condition:
An export condition was built first and removed. Both work; the specifier wins because it is a line you can read.
grepanswers which runtime a build is on, and a mismatch reads as a wrong-looking import rather than as a resolve failure two layers down. The condition also neededcustomConditionsin every consumer's tsconfig to keep tsc agreeing with the bundler.The plugins get two more names rather than an option, because the choice has to agree with
pluginSolid2()and with the installedsolid-js, and a name that must match its neighbour is easier to get right than a flag that must:Three things the migration guide did not say
Read out of
solid-js@2.0.0-rc.0and@solidjs/signals@2.0.0-rc.0rather than out of the guide, and all three changed the plan:Dynamicsurvived unchanged. The plan predicted a rewrite to adynamic(source)factory.dynamic()exists, but so doesDynamic, and@solidjs/webre-exportscreateComponentfromsolid-js, so the renderer needs the same two names in both majors.JSXis gone fromsolid-js. Core no longer declares the namespace; it belongs to the renderer now. This is why the renderer module carries the type as well as the two values.useContextthrows in the implementation.getContextthrowsContextNotFoundErrorwhen the resolved value isundefined, and a provider supplyingundefinedcounts as having provided, so an empty provider shadows a real one above it with a throw.defineComponentnow skips the wrapper when the setup returned no context: a correctness fix under 2.0 and a no-op under 1.9.Verified
145 runtime tests under 1.9, unchanged. Runtime typecheck clean. Both builds emitted. 68 Rust tests, 22 compiler JS tests. The boundary specifier checked end to end through the rebuilt native binding in both bundle mode and source mode, the latter being the one
@pathscale/uiuses.Not verified
The 2.0 build has never run against an installed
solid-js@2. It typechecks withskipLibCheck, so its declarations bind to@solidjs/webbut nothing has checked@solidjs/webagainst the Solid 2 it expects. The first real test is a@pathscale/uibuild on Solid 2.Separately and not fixed here:
@rsbuild/plugin-solid@1.2.2depends onbabel-preset-solid: ^1.9.12, a hard pin on Solid 1's JSX transform, and 11@solid-primitives/*peer-pinsolid-js: ^1.6.12and callcreateEffect/onCleanupinternally. Neither is fixable from this repository.SOLID-2-PLAN.mdcarries the full record.